// ECMAScript(ES)是规范、 JavaScript是ES的实现
// ES6的第一个版本在2015年6月发布, 正式名称是《ECMAScript 2015 标准》(简称ES2015)

// 1.let
// 推荐使用let关键字替代var关键字声明变量, 因为var存在诸多问题, 比如:
// 1.1.越域
{
  var a = 1;
  let b = 2;
}
console.log(a); // 1
// console.log(b); // ReferenceError: b is not defined

// 1.2.重复声明
// var可以声明多次, let只能声明一次
var m = 1;
var m = 2;
let n = 3;
// let n = 4;
console.log(m); // 2
console.log(n); //  Identifier 'n' has already been declared

// 1.3.变量提升
// var会变量提升, let不存在变量提升
console.log(x); // undefined
var x = 10;
// console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 20;

// 2.const
// const声明之后不允许改变, 且一旦声明必须初始化
const t = 1;
// t = 2; // TypeError: Assignment to constant variable

// 3.解构
// 3.1.数组解构
let arr = [100, 200, 300];
const [i, j, k] = arr; // i, j, k将与arr中的每个位置对应来取值
console.log(i, j, k);

// 3.2.对象解构
const person = {
  nickname: "miranda",
  age: 18,
  email: "123@qq.com",
  language: ['java', 'python', 'php']
}
const {nickname, age, language} = person; // 解构表达式获取值, 将person里面每一个属性和左边对应赋值
console.log(nickname, age, language);

const {nickname: aa, age: bb, language: cc} = person; // 如果想要将nickname的值赋值给其他变量, 可以如下, aa是新的变量名
console.log(aa, bb, cc);

function test({nickname, age, language}) {
  console.log(nickname);
  console.log(age);
  console.log(language);
}

test(person);

// 4.链判断
// 如果读取对象内部的某个属性, 往往需要判断一下属性的上层对象是否存在
let data = {
  body: {
    user: {
      firstName: "miranda"
    }
  }
};
// 错误写法
// const firstName = data.body.user.firstName || 'default';
// console.log(firstName); // Cannot read properties of null (reading 'body')

// 正确写法
// const firstName = (data && data.body && data.body.user && data.body.user.firstName) || 'default';
// console.log(firstName);

// 链判断运算符 ?. 简化上面的写法
const firstName = data?.body?.user?.firstName || 'default';
console.log(firstName);

// 5.参数默认值
// 在ES6以前, 我们无法给一个函数参数设置默认值, 只能采用变通写法
function add1(a, b) {
  // 判断b是否为空, 为空就赋默认值1
  b = b || 1;
  return a + b;
}
console.log(add1(10));

// 现在可以这么写, 直接给参数写上默认值, 没传就会自动使用默认值, 注意默认值必须放在最后一个参数
function add2(a, b = 1) {
  return a + b;
}
console.log(add2(10));

// 6.箭头函数
function print1(arg) {
  console.log(arg);
}

let print2 = function(arg) {
  console.log(arg);
}

let print3 = arg => console.log(arg);

print1(10);
print2(20);
print3(30);

let sum1 = (a, b) => a + b;
console.log(sum1(40, 20));

let sum2 = (a, b) => {
  const sum = a + b;
  return sum;
}
console.log(sum2(40, 20));

// 7.模板字符串
let info1 = "你好, 我的名字是: " + person.nickname + ", 年龄是: " + person.age + ", 邮箱是: " + person.email;
console.log(info1);

// 模板字符串的写法
let info2 = `你好, 我的名字是: ${person.nickname}, 年龄是: ${person.age}, 邮箱是: ${person.email}`;
console.log(info2);

// 8.Promise
// fetch是浏览器支持从远程获取数据的一个函数, 这个函数返回的就是Promise对象
const url = "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json";
const promise = fetch(url);
console.log(1);

promise.then(response => {
  let status = response.status;
  console.log("响应状态码: ", status);

  let json = response.json();
  console.log("响应体json数据: ", json);

  json.then(res => {
    console.log(res);
  });
}).catch(error => {
  console.log("远程调用失败: ", error);
});

console.log(2);

function func1(t) {
  return new Promise((resolve, reject) => {
    if(t % 2 == 0) {
      resolve(t);
    } else {
      reject(`${t}不是偶数`);
    }
  });
};

func1(100).then(data => {
  console.log("data: ", data);
}).catch(err => {
  console.log(err);
});

// 9.Async函数
async function func2(t) {
  if(t % 2 == 0) {
    return t;
  } else {
    throw new Error(`${t}不是偶数`);
  }
};
func2(201).then(data => {
  console.log("data: ", data);
}).catch(err => {
  console.log(err);
});

async function getProductList() {
  // 在这一行之后, 函数将等待fetch调用完成
  // 调用fetch将返回一个响应或抛出一个错误
  let promise1 = await fetch(url);
  console.log("promise1: ", promise1);
  // 在这一行之后, 函数将等待promise1.json()调用完成
  // 调用promise1.json()将返回一个json对象或抛出一个错误
  let promise2 = await promise1.json();
  console.log("promise2: ", promise2);
}
getProductList();

// 10.展开运算符(一种非常强大且灵活的语法糖, 它使用三个连续的点...表示, 广泛应用于数组和对象操作中)
// 10.1.展开数组
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5, 6];
console.log("展开数组: ", arr2); // 输出: [1, 2, 3, 4, 5, 6]

// 10.2.展开对象
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 };
console.log("展开对象: ", obj2); // 输出: { a: 1, b: 2, c: 3 }

// 10.3.在函数中使用
function sumTest(a, b, c) {
  return a + b + c;
}
const numbers = [1, 2, 3];
console.log("sumTest: ", sumTest(...numbers)); // 输出: 6

// 10.4.合并数组
const arr3 = [1, 2, 3];
const arr4 = [4, 5, 6];
const arr5 = [...arr3, ...arr4];
console.log("arr5: ", arr5); // 输出: [1, 2, 3, 4, 5, 6]

// 10.5.数组的浅拷贝
const arr6 = [1, 2, 3];
const arrCopy = [...arr6];
console.log("arrCopy: ", arrCopy); // 输出: [1, 2, 3]

// 10.6.合并对象
const obj3 = { a: 1, b: 2 };
const obj4 = { c: 3, d: 4 };
const obj5 = { ...obj3, ...obj4 };
console.log("obj5: ", obj5); // 输出: { a: 1, b: 2, c: 3, d: 4 }

// 10.7.对象的浅拷贝
const obj6 = { a: 1, b: 2 };
const obj7 = obj6;
const objCopy = { ...obj6 };
console.log("objCopy: ", objCopy); // 输出: { a: 1, b: 2 }
console.log("obj6和obj7是否是同一对象: ", obj6 === obj7); // 输出: true
console.log("obj6和objCopy是否是同一对象: ", obj6 === objCopy); // 输出: false

// 10.8.更新对象属性(使用展开运算符可以方便地更新对象的属性, 而不改变原对象)
const obj8 = { a: 1, b: 2 };
const updatedObj = { ...obj8, b: 3 };
console.log("obj8: ", obj8); // 输出: { a: 1, b: 2 }
console.log("updatedObj: ", updatedObj); // 输出: { a: 1, b: 3 }